package org.dubnation.car.service.impl;

import org.dubnation.auth.context.LoginContext;
import org.dubnation.base.constants.SystemConstants;
import org.dubnation.base.enums.ResponseCode;
import org.dubnation.base.exception.BusinessException;
import org.dubnation.base.util.AjaxResult;
import org.dubnation.base.util.SendEmailUtil;
import org.dubnation.base.util.audit.BaiduAuditUtils;
import org.dubnation.base.util.audit.DubnationAssert;
import org.dubnation.base.util.audit.ErrorInfo;
import org.dubnation.base.util.velocity.VelocityUtils;
import org.dubnation.car.domain.*;
import org.dubnation.car.esdoc.CarDoc;
import org.dubnation.car.mapper.CarDetailMapper;
import org.dubnation.car.mapper.CarMapper;
import org.dubnation.car.mapper.CarOnlineAuditLogMapper;
import org.dubnation.car.repository.CarDocRepository;
import org.dubnation.car.service.ICarService;
import org.dubnation.base.service.impl.BaseServiceImpl;
import org.dubnation.org.domain.Employee;
import org.dubnation.org.domain.Shop;
import org.dubnation.org.mapper.EmployeeMapper;
import org.dubnation.org.mapper.ShopEmployeeMapper;
import org.dubnation.org.service.IEmployeeService;
import org.dubnation.user.domain.User;
import org.dubnation.user.rabbitmq.entity.SendEntity;
import org.elasticsearch.common.geo.GeoPoint;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author zhu
 * @since 2023-11-02
 */
@Service
@Transactional
public class CarServiceImpl extends BaseServiceImpl<Car> implements ICarService {

    @Autowired(required = false)
    private ShopEmployeeMapper shopEmployeeMapper;

    @Autowired(required = false)
    private CarDetailMapper carDetailMapper;

    @Autowired(required = false)
    private CarMapper carMapper;

    @Autowired
    private CarDocRepository carDocRepository;

    @Autowired
    private CarOnlineAuditLog carOnlineAuditLog;

    @Autowired(required = false)
    private CarOnlineAuditLogMapper carOnlineAuditLogMapper;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired
    private ErrorInfo errorInfo;

    @Autowired
    private IEmployeeService employeeService;

    @Autowired
    private SendEntity sendEntity;

    @Autowired(required = false)
    private EmployeeMapper employeeMapper;

    @Value("${car.audit.reject-path}")
    private String auditRejectPath;

    @Value("${velocity.template-path}")
    private String templatePath;

    @Value("${velocity.target-path}")
    private String targetPath;

    /**
     * 重写新增方法
     * @param car
     */
    public void insertCar(Car car, String token) {
        /*
        * 车辆维护
        * */
        //获取登录对象，根据登录对象查询到对应店铺
        Object loginAdmin = LoginContext.getLoginAdmin();
        if (loginAdmin instanceof User) throw new BusinessException("您没有该操作权限!");
        Employee employee = (Employee) loginAdmin;
        Shop shop = shopEmployeeMapper.queryByEmpId(employee.getId());
        car.setShopId(shop.getId());
        car.setShopName(shop.getName());
        car.setState(Car.OFF_SHELF);  //设置下架
        car.setSellerId(employee.getId()); //登录者即为售卖者
        carMapper.insert(car);

        Car dataBaseCar = carMapper.queryByTitle(car.getTitle());
        DubnationAssert.isNull(dataBaseCar, ResponseCode.RESPONSE_CODE_4000017);
        //1.对数据进行审核, 审核失败也不停止程序, 将错误信息存储到redis中方便查看
        String[] splitCarInfos = car.getCarDetail().getInfo().split("</p>");
        StringBuilder infoTextAudit = new StringBuilder();  //用于拼接细节文本数据
        StringBuilder errMsg = new StringBuilder(); //车辆数据的错误信息
        List<String> imgAuditList = new ArrayList<>(); //存储图片审核的数组
        for (String carInfo : splitCarInfos) {
            //细节信息中有文本数据
            Pattern pattern = Pattern.compile("[\\u4e00-\\u9fa5]"); // 定义中文字符范围的正则表达式
            Matcher matcher = pattern.matcher(carInfo); // 创建Matcher对象
            while (matcher.find()) { // 查找所有匹配项
                infoTextAudit.append(matcher.group()).toString();
            }
            if (carInfo.contains("<img")){
                //细节信息中有图片数据
                String img = carInfo.substring(carInfo.indexOf("=") + 2, carInfo.lastIndexOf(">") - 1);
                imgAuditList.add(img);
            }
        }
        imgAuditList.add(car.getCover());
        List<String> textAuditList = null;
        if (infoTextAudit.toString().equals("")) textAuditList = Arrays.asList(car.getTitle());
        else textAuditList = Arrays.asList(car.getTitle(), infoTextAudit.toString());
        List<Map<String, Object>> censorResults = BaiduAuditUtils.censor(textAuditList, imgAuditList);
        censorResults.forEach(censorResult -> {
            if (!Boolean.valueOf(censorResult.get("success").toString())){
                //审核数据出现错误
                String message = censorResult.get("message").toString();
                if (errMsg.length() > 0){
                    errMsg.append(", " + message);
                }else {
                    errMsg.append(message);
                }
            }
        });
        Boolean isError = false;
        if (!errMsg.equals("")){
            isError = true;
            car.setAuditState(SystemConstants.Car.AUDIT_FAIL);
            carOnlineAuditLog.setState(SystemConstants.CarOnlineAuditLog.AUDIT_FAIL);
        }else{
            car.setAuditState(SystemConstants.Car.AUDIT_SUCCESS);
            carOnlineAuditLog.setState(SystemConstants.CarOnlineAuditLog.AUDIT_SUCCESS);
        }
        //2.更改数据, 存储到数据库中
        car.setShopId(car.getShop().getId());
        car.setCreateTime(new Date());
        car.setState(SystemConstants.Car.OFF_SALE);
        car.setShopName(car.getShop().getName());
        super.insert(car);
        //3.记录审核日志
        carOnlineAuditLog.setCarId(car.getId());
        carOnlineAuditLog.setAuditId(null);
        carOnlineAuditLog.setAuditTime(new Date());
        String note = "admin添加了id=%s的车辆";
        carOnlineAuditLog.setNote(String.format(note, car.getId()));
        carOnlineAuditLogMapper.insert(carOnlineAuditLog);
        if (isError.equals(true)){
            String str = "车辆%s%s";
            redisTemplate.opsForValue().set(car.getId(), String.format(str, car.getId(), errMsg.toString()));
        }
    }

    /**
     * 重写修改方法
     * @param car
     */
    @Override
    public void update(Car car) {
        /*
        * 车辆数据维护
        * */
        if (car.getOnSaleTime() != null && car.getOffSaleTime() != null){
            car.setOnSaleTime(null);
            car.setOffSaleTime(null);
            car.setState(0L);
        }
        if (car.getTypeId() == null){
            car.setTypeId(car.getCarType().getId());
        }
        car.setAuditState(SystemConstants.Car.AUDIT_SUCCESS);
        //若车辆已上架则不予修改
        if (car.getState().equals(SystemConstants.Car.ON_SALE)){
            throw new BusinessException(ResponseCode.RESPONSE_CODE_4000025);
        }
        super.update(car);

    }

    /**
     * 根据id查询指定车辆信息
     * @param id
     * @return
     */
    @Override
    public CarInfoVO getCarInfo(Long id) {
        CarInfoVO carInfo = carMapper.getCarInfo(id);
        //获取该车辆营销人员所售卖的车辆数量
        List<Car> cars = carMapper.getSellNum(carInfo.getEmployee().getId());
        carInfo.setSellNum(cars.size());
        return carInfo;
    }

    /**
     * 车辆上架
     * @param ids
     */
    @Override
    @Transactional
    public AjaxResult onSale(List<Long> ids, HttpServletRequest request) {
        //1.判断id数组是否为空
        if (ids.size() == 0 || ids.size() < 1){
            throw new BusinessException(ResponseCode.RESPONSE_CODE_4000023);
        }
        //2.查询id对应的车辆数据, 并判断是否为要上架的车辆
        List<Car> cars = carMapper.queryByIds(ids);
        StringBuffer carNotExist = new StringBuffer(); //用于返回不存在的车辆
        StringBuffer carArlOnSale = new StringBuffer(); //用于返回已经上架的车辆
        AtomicReference<Boolean> auditSuccess = new AtomicReference<>(true);
        AtomicReference<Boolean> detectionSuccess = new AtomicReference<>(true);
        cars.forEach(car -> {
            if (!ids.contains(car.getId())){
                //要上架的车辆在数据库中不存在
                if (carNotExist.equals("")){
                    carNotExist.append(car.getId());
                }else {
                    carNotExist.append("," + car.getId());
                }
            }
            //3.判断对应车辆是否已上架, 若已上架将已上架的车辆返回给前端, 并不再将其上架
            if (SystemConstants.Car.ON_SALE.equals(car.getState())){
                //该车辆已上架
                if (carArlOnSale.equals("")){
                    carArlOnSale.append(car.getId());
                }else {
                    carArlOnSale.append(", " + car.getId());
                }
            }
            //4.根据车辆数据中的审核状态和车辆检测结果判断车辆是否可以上架
            if (SystemConstants.Car.AUDIT_FAIL.equals(car.getState())){
                //车辆审核失败
                auditSuccess.set(false);
            }
            if (!SystemConstants.CarDetail.DISCONTENT_ON_SALE.equals(car.getCarDetail().getMeetOnSaleRequirements())){
                //车辆不满足上架条件, 获取检查结果
                detectionSuccess.set(false);
            }
        });
        //5.若不符合上架条件则将审核违规信息和检测失败结果以邮件的形式发送给商家或返回前端
        if (!carNotExist.equals("")){
            String str = "车辆: %s不存在!";
            throw new BusinessException(String.format(str, carNotExist.toString()));
        }
        if (!carArlOnSale.equals("")){
            String str = "车辆: %s已上架, 请勿重复上架!";
            throw new BusinessException(String.format(str, carArlOnSale.toString()));
        }
        if (!auditSuccess.get()){
            cars.forEach(car -> {
                String errMsg = (String) redisTemplate.opsForValue().get(car.getId());
                errorInfo.setDetailedMsg(errMsg);
                errorInfo.setAdminId(car.getSellerId());
                //获取登录员工
                Object loginAdmin = LoginContext.getLoginAdmin();
                if (loginAdmin instanceof Employee){
                    try {
                        //给商家发送车辆审核失败邮件
                        Employee seller = employeeMapper.queryById(car.getSellerId());
                        if (Objects.nonNull(seller.getEmail())){
                            sendEntity.setFrom(((Employee) loginAdmin).getEmail());
                            sendEntity.setTo(seller.getEmail());
                            sendEntity.setSubject(errMsg);
                            sendEntity.setContent(String.format("请点击链接进行车辆信息修改: %s", auditRejectPath));
                            SendEmailUtil.sendEmail(sendEntity);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        if (!detectionSuccess.get()){
            Map<String, List<Map<Long, List<byte[]>>>> detectionMap = new HashMap<>(); //返回检测不通过的车辆检测图片集
            List<Map<Long, List<byte[]>>> detections = new ArrayList<>();
            cars.forEach(car -> {
                List<String> detectionList = new ArrayList<>(); //存储carDetail对象的检测属性值
                CarDetail carDetail = car.getCarDetail();
                //使用反射来获取carDetail对象的属性值
                Field[] fields = carDetail.getClass().getDeclaredFields();
                for (Field field : fields) {
                    field.setAccessible(true);
                    try {
                        String detectionResult = (String) field.get(carDetail); //通过反射获取字段值
                        detectionList.add(detectionResult);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
                // 正则表达式匹配图片Base64编码
                List<byte[]> imgDataList = new ArrayList<>(); //用于存储检测图片返回前端
                Map<Long, List<byte[]>> imgDataMap = new HashMap<>();
                String pattern = "<img\\s+src\\s*=\\s*\"data:image\\/([^;]+);base64,([^\"]+)\"";
                Pattern imgPattern = Pattern.compile(pattern);
                detectionList.forEach(detection -> {
                    //循环获取每个字段值中的图片地址
                    Matcher matcher = imgPattern.matcher(detection);
                    // 提取图片Base64编码
                    while (matcher.find()) {
                        String base64Image = matcher.group(2);
                        // 将Base64编码转换为图片
                        byte[] imageData = Base64.getDecoder().decode(base64Image);
                        imgDataList.add(imageData);
                        imgDataMap.put(car.getId(), imgDataList);
                        detections.add(imgDataMap);
                    }
                });
            });
            detectionMap.put("以下是检测之后不满足上架要求的车辆检测图片", detections);
            return AjaxResult.me().setSuccess(false)
                                  .setCode(ResponseCode.RESPONSE_CODE_4000024.getCode())
                                  .setMessage(ResponseCode.RESPONSE_CODE_4000024.getMessage())
                                  .setData(detectionMap);
        }
        //6.若可以上架则将车辆数据转换为符合es存储条件的数据形式并上架, Car->CarDoc
        List<CarDoc> carDocs = convertCarDoc(cars, request);
        carDocRepository.saveAll(carDocs); //存储数据到es
        //7.修改车辆的上架时间和状态
        carMapper.updateOnSaleByIds(ids);
        //9.生成对应车辆详情静态页面
        cars.forEach(car -> {
            CarInfoVO carInfo = carMapper.getCarInfo(car.getId());
            List<Car> carList = carMapper.getSellNum(carInfo.getEmployee().getId());
            carInfo.setSellNum(carList.size());
            VelocityUtils.staticByTemplate(carInfo, templatePath, targetPath + car.getId() + ".html"); //生成静态页面
        });
        return null;
    }

    /**
     * 将Car数据转换为CarDoc数据
     * @param cars
     * @return
     */
    @Transactional
    public List<CarDoc> convertCarDoc(List<Car> cars, HttpServletRequest request) {
        List<CarDoc> carDocs = new ArrayList<>();
        cars.forEach(car -> {
            CarDoc carDoc = new CarDoc();
            BeanUtils.copyProperties(car, carDoc);
            carDoc.setShopAddress(car.getShop().getAddress());
            GeoPoint geoPoint = new GeoPoint(car.getShop().getLat(), car.getShop().getLng());
            carDoc.setShopPoint(geoPoint);
            carDoc.setTypeName(car.getCarType().getName());
            carDoc.setCarInfo(car.getCarDetail().getInfo());
            carDocs.add(carDoc);
            //8.记录上架日志
            //判断该车辆是否已有日志, 若没有新增, 有则修改即可
            CarOnlineAuditLog onlineAuditLog = carOnlineAuditLogMapper.queryByCarId(car.getId());
            Object loginAdmin = LoginContext.getLoginAdmin();
            if (!(loginAdmin instanceof Employee)){
                throw new BusinessException("您没有此操作权限!");
            }
            String note = String.format("%s上架了id=%s的车辆", ((Employee) loginAdmin).getRealName(), car.getId());
            if (Objects.isNull(onlineAuditLog)){
                //新增
                carOnlineAuditLog.setCarId(car.getId());
                carOnlineAuditLog.setState(CarOnlineAuditLog.SUCCESS);
                carOnlineAuditLog.setNote(note);
                carOnlineAuditLogMapper.insert(carOnlineAuditLog);
            }else {
                //修改
                onlineAuditLog.setCarId(car.getId());
                onlineAuditLog.setState(CarOnlineAuditLog.SUCCESS);
                onlineAuditLog.setNote(note);
                carOnlineAuditLogMapper.update(onlineAuditLog);
            }
        });
        return carDocs;
    }

    /**
     * 车辆下架
     * @param ids
     */
    @Override
    public void offSale(List<Long> ids) {
        //1.根据id数组从es索引库获取对应车辆数据
        Iterable<CarDoc> carDocs = carDocRepository.findAllById(ids);
        DubnationAssert.isNotNull(carDocs, ResponseCode.RESPONSE_CODE_4000018);
        //2.查看该车辆是否已下架, 若已下架则无需重复下架
        List<Car> cars = carMapper.queryByIds(ids);
        StringBuffer alrOnSale = new StringBuffer();
        cars.forEach(car -> {
            if (SystemConstants.Car.OFF_SALE.equals(car.getState())){
                if (alrOnSale.equals("")){
                    alrOnSale.append(car.getId());
                }else {
                    alrOnSale.append(", " + car.getId());
                }
            }
        });
        if (!alrOnSale.equals("")){
            throw new BusinessException(alrOnSale.toString());
        }
        //3.删除车辆数据
        carDocRepository.deleteAll(carDocs);
        //4.修改车辆下架时间offsaletime, 状态state, 审核状态auditstate
        carMapper.updateOffSaleByIds(ids);
        cars.forEach(car -> {
            //5.将下架车辆的静态页面文件删除
            File file = new File(targetPath + car.getId() + ".html");
            file.delete();
            //6.记录车辆下架日志
            carOnlineAuditLog.setCarId(car.getId());
            carOnlineAuditLog.setState(CarOnlineAuditLog.SUCCESS);
            carOnlineAuditLog.setAuditId(null);
            carOnlineAuditLog.setAuditTime(new Date());
            carOnlineAuditLog.setNote("车辆id:" + car.getId() + "已下架!");
            carOnlineAuditLogMapper.insert(carOnlineAuditLog);
        });
    }
}
